﻿using Qing.Lang;
using Qing.Std;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Qing.Proto {
    /*对原生套接字的封装*/
    public class PortObj : Obj {

        public PortObj(Ctx ctx) : base(ctx) {
            this.Raw = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.init(ctx);
        }

        public PortObj(Socket socket, Ctx ctx) : base(ctx) {
            this.Raw = socket;
            this.init(ctx);
        }

        public PortObj(string protocl, Ctx ctx) : base(ctx) {
            this.Raw = NewSocket(protocl);
            this.init(ctx);
        }

        public Socket Raw {
            get { return (Socket)base.Raw; }
            set { base.Raw = value; }
        }

        public override string toStr(string tp = "") {
            return base.toStr("#套接字");
        }


        public static Socket NewSocket(string protocol) {
            ProtocolType pt = ProtocolType.Tcp;
            if (protocol == "udp" || protocol == "UDP" || protocol == "Udp") {
                pt = ProtocolType.Udp;
            }

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, pt);
            return socket;
        }


        public void init(Ctx ctx) {
            Map["@处理连接"] = new Parser().Parse("@【】｛｝", Env.LibCtx!)[0].Eval(ctx);
            Map["@处理消息"] = new Parser().Parse("@【】｛｝", Env.LibCtx!)[0].Eval(ctx);
            Map["#接收缓存长度"] = new Expr(TP.Int, 2048);
            Map["#是主机"] = new Expr(TP.Bool, false);
            Map["#运行中"] = new Expr(TP.Bool, false);
            Map["#已关闭"] = new Expr(TP.Bool, false);

            Map["@绑定"] = new Expr( new PortBind());
            Map["@连接"] = new Expr(new PortConnect());
            Map["@监听"] = new Expr(new PortListen());
            Map["@等待连接"] = new Expr( new PortAccept());
            Map["@启动"] = new Expr(new PortStart());
            Map["@停止"] = new Expr(new PortStop());
            Map["@发送"] = new Expr(new PortSend());
            Map["@接收"] = new Expr(new PortReceive());
            Map["@关闭"] = new Expr(new PortClose());
            Map["@远端地址"] = new Expr(new PortRemote());
        }


        class PortBind : Native {
            public override string Name { get; set; } = "绑定";
            public override string Desc { get; set; } = "参数1-字符串，返回逻辑；根据传入的套接字路径，绑定端口";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {

                if (args.Count > 0 && args[0].Tp == TP.Str) {
                    try {
                        IPEndPoint iped = IPEndPoint.Parse(args[0].Str());

                        ((PortObj)obj!).Raw.Bind(iped);
                        obj.PutNow("#是主机", new Expr(TP.Bool, true));
                        return new Expr(TP.Bool, true);
                    } catch (Exception e) {
                        return Expr.Err("套接字绑定端口失败=>" + e.Message);
                    }

                }


                return Expr.Err("套接字类型·绑定方法参数错误");
            }

        }

        class PortConnect : Native {
            public override string Name { get; set; } = "连接";
            public override string Desc { get; set; } = "参数1-字符串，返回逻辑；根据传入的套接字路径，进行连接";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {

                if (args.Count > 0 && args[0].Tp == TP.Str) {
                    try {
                        IPEndPoint iped = IPEndPoint.Parse(args[0].Str());

                        ((PortObj)obj!).Raw.Connect(iped);
                        obj.PutNow("#是主机", new Expr(TP.Bool, false));
                        return new Expr(TP.Bool, true);
                    } catch (Exception e) {
                        return Expr.Err("套接字连接失败=>" + e.Message);
                    }

                }


                return Expr.Err("套接字类型·连接方法参数错误");
            }

        }

        class PortListen : Native {
            public override string Name { get; set; } = "监听";
            public override string Desc { get; set; } = "可选参数1-整数，返回逻辑；根据传入的端口号，开始监听端口";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                int count = 0;
                if (args.Count > 0 && args[0].Tp == TP.Int) {
                    count = args[0].Int();
                }
                ((PortObj)obj!).Raw.Listen(count);
                return new Expr(TP.Bool, true);
            }

        }

        class PortAccept : Native {
            public override string Name { get; set; } = "等待连接";
            public override string Desc { get; set; } = "无参，返回对象；监听中的端口，等待新的连接，返回套接字对象";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                Socket conn = ((PortObj)obj!).Raw.Accept();
                return new Expr(TP.Obj, new PortObj(conn, ctx));
            }
        }


        class PortStart : Native {
            public override string Name { get; set; } = "启动";
            public override string Desc { get; set; } = "无参数，返回空；设置好套接字的监听响应逻辑后，启动循环监听";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                Socket socket = ((PortObj)obj!).Raw;
                obj!.PutNow("#运行中", new Expr(TP.Bool, true));
                bool isHost = obj!.GetNow("#是主机").ToBool();
                Expr runningExpr = obj!.GetNow("#运行中");
                if (isHost) {
                    while (runningExpr.ToBool()) {
                        Socket conn = socket.Accept();
                        Expr connExpr = new Expr(TP.Obj, new PortObj(conn, ctx));
                        Func func = obj.GetNow("@处理连接").Func();
                        Expr ans = func.Run(new List<Expr> { connExpr }, ctx);
                        if (ans.Tp == TP.Err) {
                            ans.Echo();
                        }
                    }
                } else {
                    int emptyCount = 0;
                    int n = obj!.GetNow("#接收缓存长度").Int();
                    while (socket.Connected && runningExpr.ToBool()) {

                        byte[] buffer = new byte[n];
                        try {
                            n = socket.Receive(buffer);
                        } catch (Exception e) {
                            //Console.WriteLine(e.Message);
                            runningExpr.Val = false;
                            Expr closed = obj!.Get("#已关闭");
                            lock (closed) {
                                closed.Val = false;
                            }
                            socket.Close();
                            break;
                        }

                        if (n == 0) {
                            emptyCount++;
                            if (emptyCount >= 2) {
                                runningExpr.Val = false;
                                Expr closed = obj!.Get("#已关闭");
                                lock (closed) {
                                    closed.Val = false;
                                }
                                socket.Close();
                                break;
                            }

                        } else {
                            emptyCount = 0;
                        }

                        Expr mesExpr = new Expr(TP.Bin, buffer[0..n]);

                        Func func = obj.GetNow("@处理消息").Func();
                        Expr ans = func.Run(new List<Expr> { mesExpr }, ctx);
                        if (ans.Tp == TP.Err) {
                            ans.Echo();
                        }
                    }
                }


                return new Expr();
            }

        }

        class PortStop : Native {
            public override string Name { get; set; } = "停止";
            public override string Desc { get; set; } = "无参数，返回逻辑；停止循环监听";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                Expr running = obj!.GetNow("#运行中");
                lock (running) {
                    running.Val = false;
                }
                return new Expr(TP.Bool, true);
            }

        }


        class PortSend : Native {
            public override string Name { get; set; } = "发送";
            public override string Desc { get; set; } = "参数1-字符串|二进制，返回整数；通过打开的套接字传送数据，返回传送的字节数";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                if (args.Count == 0) {
                    return Expr.Err("套接字类型·发送方法参数不足");
                }
                Socket socket = ((PortObj)obj!).Raw;

                if (obj.GetNow("#已关闭").ToBool()) {
                    Console.WriteLine("Socket连接已关闭，无法发送数据");
                    return new Expr(TP.Int, 0);
                }

                if (args[0].Tp == TP.Bin) {
                    try {
                        int n = socket.Send(args[0].Bin());
                        return new Expr(TP.Int, n);
                    } catch (Exception e) {
                        Console.WriteLine(e.Message);
                        Expr closed = obj!.Get("#已关闭");
                        lock (closed) {
                            closed.Val = false;
                        }
                        return new Expr(TP.Int, 0);
                    }

                }
                if (args[0].Tp == TP.Str) {
                    Encoding ec = Encoding.UTF8;
                    if (args.Count > 1 && args[1].Tp == TP.Str) {
                        ec = EncodeUtil.parseEncode(args[1].Str());
                    }

                    try {
                        int n = socket.Send(ec.GetBytes(args[0].Str()));
                        return new Expr(TP.Int, n);
                    } catch (Exception e) {
                        Console.WriteLine(e.Message);
                        Expr closed = obj!.Get("#已关闭");
                        lock (closed) {
                            closed.Val = false;
                        }
                        return new Expr(TP.Int, 0);
                    }

                }

                return Expr.Err("套接字类型·发送方法参数错误");
            }

        }

        class PortReceive : Native {
            public override string Name { get; set; } = "接收";
            public override string Desc { get; set; } = "可选参数1-整数，返回二进制；从打开的套接字接收数据，传入的整数作为缓存长度";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                int n = obj!.GetNow("#接收缓存长度").Int();
                if (args.Count > 0 && args[0].Tp == TP.Int) {
                    n = args[0].Int();
                }

                byte[] buffer = new byte[n];
                ((PortObj)obj!).Raw.Receive(buffer);

                return new Expr(TP.Bin, buffer[0..n]);
            }

        }


        class PortClose : Native {
            public override string Name { get; set; } = "关闭";
            public override string Desc { get; set; } = "无参，返回逻辑；关闭套接字";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
                Expr running = obj!.GetNow("#运行中");
                if (running.ToBool()) {
                    lock (running) {
                        running.Val = false;
                    }
                }
                ((PortObj)obj!).Raw.Close();
                Expr closed = obj!.Get("#已关闭");
                lock (closed) {
                    closed.Val = false;
                }
                return new Expr(TP.Bool, true);
            }

        }

        class PortRemote : Native {
            public override string Name { get; set; } = "远端地址";
            public override string Desc { get; set; } = "无参，返回字符串；获取套接字远端的地址";
            public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {

                return new Expr(TP.Str, ((PortObj)obj!).Raw.RemoteEndPoint?.ToString() ?? "");
            }

        }

    }
}
